#include <syslog.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <errno.h>
#include <netdb.h>
#include <ctype.h>
#include <stdio.h>
#include <signal.h>
#include <sys/wait.h>
#include <fcntl.h>

#include <unistd.h>
#include <string.h>
#include <stddef.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <time.h>
#include <pthread.h>
#include <netinet/ip6.h>
#include <stdlib.h>

#include "tunnet.h"
#include "socks.h"
#include "config.h"


#define _IN_MAIN
#include "global.h"

#define MAXFD 64

char * service_file=NULL;
char * route_file=NULL;
char * pid_file=NULL;
char * f_info_log=NULL;
char * f_access_log=NULL;

int f_daemon=0;

void clean();

void * Service_HTTP(void *threadArgs_HTTP);
int neighbor_exchange();

int start_service( struct service *sp,unsigned char srvcType);
void * Service_HTTP(void *threadArgs_HTTP);

int initialize(){
    int i;
    pid_t pid;
    FILE * f_pid_file;
 
    services.first=NULL;
    services.number=0;

    neighbors.first=NULL;
    neighbors.number=0;

    routes.first=NULL;
    routes.number=0;

    acl4.first=NULL;
    acl4.number=0;

    if(f_daemon){
	if( (pid=fork()) <0){
		fprintf(stderr, "Error on fork1() in inilialize() \n");
		return -1;
	}
	else{
		if(pid) _exit(0); // parent process
	}
	if( setsid()<0) {
		fprintf(stderr, "Error on setsid() in inilialize() \n");
		return -1;
	}
	signal(SIGHUP, SIG_IGN);
        if( (pid=fork()) <0) {
		fprintf(stderr, "Error on fork2() in inilialize() \n");
		return -1;
	}
	else {
		if (pid) _exit(0);
	}
	//chdir("/");
	for(i=0;i<MAXFD;i++) 
		close(i);
	open("/dev/null", O_RDONLY);
	open("/dev/null", O_WRONLY);
	open("/dev/null", O_RDWR);

    }
    signal(SIGINT, clean);

    signal(SIGPIPE, SIG_IGN);	
    /*
      	The kernel will send the SIGPIPE signal when the remote end closes or shuts 
      	down the socket and you try to send/write. The default signal handler will 
  	terminate your program.

	One solution is to ignore the signal using 'signal(SIGPIPE, SIG_IGN)' somewhere 
	at the start of your program. You will get an EPIPE error return when send/write 
	fails due to a broken pipe.

	Another less portable solution is to pass the MSG_NOSIGNAL flag to send(). This has the same effect.
      */

    if(f_info_log){
	info_log=fopen(f_info_log,"a");
	if(info_log ==NULL) {
		fprintf(stderr, "Can not open file %s to write \n",f_info_log);
		return -1;
	}
	fseek(info_log, 0L,SEEK_END);
    }
    else{
	info_log=stderr;
    }

    if(f_access_log){
	acs_log=fopen(f_access_log,"a");
	if(acs_log ==NULL) {
		fprintf(stderr, "Can not open file %s to write \n",f_access_log);
		return -1;
	}
	fseek(acs_log, 0L,SEEK_END);
    }
    else{
	acs_log=stdout;
    }

    if(neighbor_file){ 	
	if( -1==load_neighbors(neighbor_file,&neighbors)){
	   sys_log(info_log, "\nLoad neigbhor file %s error\n",neighbor_file);
	   return -1;
	}	
    }
    else if(-1 == load_neighbor_0(&neighbors)){
	sys_log(info_log, "can not initialize neighbor 0.\n");
	return -1;
    }
    

    //load services;
    if(service_file){ 	
	if( -1==load_services(service_file,&neighbors.first->services)){
		sys_log(info_log, "\nLoad service file %s error\n",service_file);
		return -1;
	}	
    }

    //if(f_debug)neighbor_display(&neighbors);

    //load route;
    if(route_file){
        if( -1==load_route(route_file,&routes)){
                sys_log(info_log, "\nLoad route file %s error\n",route_file);
                return -1;
        }
    	if(f_debug)route_display(&routes);
    }

    
    if(pid_file){ 	
	f_pid_file=fopen(pid_file,"w");

	if ( f_pid_file!=NULL){
		pid=getpid();
		printf("pid=%d\n", pid);
		fprintf(f_pid_file,"%d",pid);
		fclose(f_pid_file);	
	} 
	else{
		fprintf(stderr,"Can not open file %s to write \n", pid_file);
		return -1;	
	}
    }
    for(i =0; i<MAX_NEIGHBORS; i++)
		openSockets[i]=-1;
    pointer=0;

    return 0 ;
}

//When I am killed or interupted, free all dynamic memories , close all log files

void clean(){
	int i;
	neighbor_dump(&neighbors);
	neighbor_clean(&neighbors);
	if(f_info_log) fclose(info_log);
	if(f_access_log) fclose(acs_log);

	for(i =0; i<pointer; i++)
                close(openSockets[i]);
	exit(-1);
}

void usage(char * program_name){
	fprintf(stderr, "%7s%-9s %s", "Usage:",program_name,
			"[-a none|pass|chap|x509] [-d] [-f] [-l log_infor]\n");
	fprintf(stderr, "%17s%s","", "[-L access_log] [-n neighbors] [-r routes] [-s service] \n");
	fprintf(stderr, "%17s%s","", "[-t timeout] [-p pid_file] [-v] \n");
	exit(-1); 
}

int main(int argc, char ** argv){
    struct service_item *sp;
    int oc ,exit_code;
    f_debug=0;
    f_report=0;
    timeout=TIMEOUT;
    authMethod=METHOD_NONE;
    neighbor_file=NULL;
    ifName=NULL;

    while((oc=getopt(argc, argv,"a:dfi:l:L:n:p:r:s:t:vD")) !=-1){
        switch(oc){
                case 'a':
			authMethod=lookup_name(auth_methods,optarg);	
			if(authMethod<0) { 
				fprintf(stderr, "unknown  method :%s\n", optarg);
				methods_display();
				
			}
                        break;
                case 'd':
                        f_debug=1;
                        break;
                case 'D':
                        f_daemon=1;
                        break;
                case 'f':
			f_report=1;
                        break;
                case 'i':
			ifName=malloc(16);
                        strncpy(ifName,optarg,16);
                        break;
                case 'l':
                        f_info_log=optarg;
                        break;
                case 'L':
                        f_access_log=optarg;
                        break;
                case 'n':
                        neighbor_file=optarg;
                        break;
                case 'p':
                        pid_file=optarg;
                        break;
                case 'r':
                        route_file=optarg;
                        break;
                case 's':
                        service_file=optarg;
                        break;
                case 't':
                        timeout=atoi(optarg);
                        break;
		case ':':
                case '?':
                        usage(argv[0]);
                        exit(1);
        }
    } //while getopt

   if(initialize()<0) exit(-1);

   start_service(&neighbors.first->services,0);


   //alarm(10);
   //signal(SIGALRM,neighbor_exchange);
   neighbor_exchange(); 

   /****************** start controller ***************/
   // It is a non-stop loop
   exit_code=start_controller(neighbors.first);

   
   sys_log(info_log, "controller exit, main function terminated, exit code=%d\n",exit_code ); 
   clean();
   
   exit(0);
    
	
}//end of main

int start_service( struct service *sp,unsigned char srvcType){
	                // if srvcType==0, start every service 
			// else start srvcType 

    struct service_item * item;
    unsigned char status;

    if(sp==NULL) return -1;
    if((sp->first==NULL) || (sp->number==0)) return -1;
     
    item=sp->first;

    while(item){
	if((srvcType !=0) && (srvcType != item->srvcType)) {
		item=item->next;
		continue;
	}
	switch(item->srvcType){
	   case SRVC_TYPE_SOCKS4:
	   case SRVC_TYPE_SOCKS5:
		//start_socks(item); 
		//ssh -g -f -N -D 10950 huhoocat@www.huhoocat.org
		system("ssh -g -f -N -D 10950 huhoocat@www.huhoocat.org");
	   break;
/*
	   case SRVC_TYPE_HTTP:
		start_http(item);
*/
	   break;
	}//switch		

	status=item->status;
	item=item->next;
    }//while

    return status;//item->status;
}



int start_socks(struct service_item * si)   //int srvcType, int port)
{
    pthread_t threadID;              /* Thread ID from pthread_create() */
    struct ThreadArgs *threadArgs;   /* Pointer to argument structure for thread */
    int res;

    if(si ==NULL) return -1;

    threadArgs=malloc(sizeof(struct ThreadArgs));
   
    if(si->srvcType ==SRVC_TYPE_SOCKS4){
	threadArgs->version=SOCKS4_VERSION;
    }
    else{
	threadArgs->version=SOCKS5_VERSION;
    }
   
    threadArgs -> port = si->relayPort;
    threadArgs -> p_srvc= si;

    res=pthread_create(&threadID, NULL, Service_Socks, (void *) threadArgs) ;
    if (res != 0)
    {
	sys_log(info_log, "Error start_socks: pthread_create returns :%d(%s) \n",res,strerror(res));
    	return -1;
    }
    return 0;

}


//-----------------------------------------------------------------------------------------------------------------
/*
int start_http(struct service_item * si)   //int srvcType, int port)
{
    
    pthread_t threadID;              // Thread ID from pthread_create() 
    int hThread;
    struct ThreadArgs_HTTP *threadArgs_HTTP;

    if(si ==NULL) return -1;
    threadArgs_HTTP=malloc(sizeof(struct ThreadArgs_HTTP));
    threadArgs_HTTP -> clntSock = ntohs((si->relayPort));
    threadArgs_HTTP -> p_srvc = si;
    hThread=pthread_create(&threadID, NULL, Service_HTTP, (void *) threadArgs_HTTP) ;
    if (hThread != 0)
    {
	sys_log(info_log, "Error start_socks: pthread_create returns :%d(%s) \n",hThread,strerror(hThread));
    	return -1;
    }
    //si->status=SRVC_STAT_START;
    return 0;
}

*/

